/*
 * 础光实时操作系统PhotonRTOS -- IOC通讯
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Rui Yang <yangrui@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

#ifdef CONFIG_IOC
static VAR(struct accurate_counter, AUTOMATIC) IpiIocCount[MAX_CPUS];
static FUNC(void, OS_CODE) SmpIocIsr(void);

FUNC(void, OS_CODE) IocInit(void)
{
    smp_set_ioc_isr(SmpIocIsr);
}

static FUNC(uint32_t, OS_CODE) IocDataSize(P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    VAR(int, AUTOMATIC) size = 0, i = 0;

    for (i = 0; i < data->ElementNum; i++) {
        size += data->Element[i].DataSize;
    }

    return size;
}

static FUNC(Std_ReturnType, OS_CODE) IocPushElement(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data, 
    P2VAR(struct ring, AUTOMATIC, OS_CONST) RingInfo, 
    VAR(bool, AUTOMATIC) queue)
{

    VAR(uint16_t, AUTOMATIC) ElementNum = 0;
    VAR(uintptr_t, AUTOMATIC) flags;
    VAR(Std_ReturnType,  AUTOMATIC) ret = IOC_E_OK;

    smp_lock_irqsave(&RingInfo->lock, flags);

    if (queue == false) {
        ring_empty(RingInfo);
    }
    /* 无论是队列发送还是非队列发送，发送长度都不应该超过队列的总长度*/
    if (ring_get_size(RingInfo) < IocDataSize(data)) {
        ret = IOC_E_LENGTH;
        smp_unlock_irqrestore(&RingInfo->lock, flags);
        return ret;
    }
    /**
     * IOC_E_LIMIT表示队列无法存储更多的数据，对于非队列实现，不应该返回IOC_E_LIMIT，
     * 但是调用IocPushElement前已经清空了队列，所以下面代码不会进入
    */
    if (ring_get_free_size(RingInfo) < IocDataSize(data)) {
        ring_set_lost_data_flag(RingInfo, true);
        ret = IOC_E_LIMIT;
        smp_unlock_irqrestore(&RingInfo->lock, flags);
        return ret;
    }
    for (ElementNum = 0; ElementNum < data->ElementNum; ElementNum++) {
        ring_push_element(RingInfo, data->Element[ElementNum].PData, \
            data->Element[ElementNum].DataSize);
    }
    smp_unlock_irqrestore(&RingInfo->lock, flags);

    return ret;
}

static FUNC(Std_ReturnType, OS_CODE) IocPopElement(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data, 
    P2VAR(struct ring, AUTOMATIC, OS_CONST) RingInfo, 
    VAR(bool, AUTOMATIC) queue)
{
    VAR(uint16_t, AUTOMATIC) ElementNum = 0;
    VAR(Std_ReturnType,  AUTOMATIC) ret = IOC_E_OK;
    VAR(uintptr_t, AUTOMATIC) flags;

    smp_lock_irqsave(&RingInfo->lock, flags);
    if (ring_get_used_size(RingInfo) < IocDataSize(data)) {
        ret = IOC_E_NO_DATA;
        smp_unlock_irqrestore(&RingInfo->lock, flags);
        return ret;
    }
    for (ElementNum = 0; ElementNum < data->ElementNum; ElementNum++) {
        ring_pop_element(RingInfo, data->Element[ElementNum].PData, \
            data->Element[ElementNum].DataSize);
    }
    /**
     * 如果曾经方产生IOC_E_LIMIT错误，接收方在读时应该返回IOC_E_LOST_DATA
    */
    if (ring_get_lost_data_flag(RingInfo) == true) {
        ring_set_lost_data_flag(RingInfo, false);
        ret = IOC_E_LOST_DATA;
    }
    smp_unlock_irqrestore(&RingInfo->lock, flags);

    return ret;
}


static FUNC(Std_ReturnType, OS_CODE) SenderParameterCheck(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    /**
     * 检查是否在task状态下
     *  非扩展状态：失败
     *  扩展状态：处于中断失能状态(对应某些模式)，失败
    */  
    if (E_OK != os_run_context_seted(ENV_TASK)) {
#if defined(EXTENDED_STATUS)
        if (irqs_disabled()) {
            return IOC_E_NOK;
        }
#else
        return IOC_E_NOK;
#endif
    }

    /**
     * 检查发送者的权限：调用IocWrite的app需要等于data->sender
    */
    if (GetApplicationID() != data->SenderId) {
        return IOC_E_NOK;
    }

    if (data->ElementNum == 0) {
        return IOC_E_LENGTH;
    }

    /**
    * 检查数据指针是否合法：查询指针的值是否在链接的段中, 目前暂时实现为判断非空
    */
    if (data->Element == 0) {
        return IOC_E_NOK;
    }

    return IOC_E_OK;
}

static FUNC(Std_ReturnType, OS_CODE) RecParameterCheck(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    /**
     * 检查发送者的权限：调用IocWrite的app需要等于data->sender
    */
    if (GetApplicationID() != data->ReceiverId) {
        return IOC_E_NOK;
    }

    if (data->ElementNum == 0) {
        return IOC_E_LENGTH;
    }

    /**
    * 检查数据指针是否合法：查询指针的值是否在链接的段中, 目前暂时实现为判断非空
    */
    if (data->Element == 0) {
        return IOC_E_NOK;
    }

    return IOC_E_OK;
}

static FUNC(Std_ReturnType, OS_CODE) TriggerRecCb(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data, \
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) PInnerQueueGroup)
{
    VAR(ApplicationStateType, AUTOMATIC) app_state;
    VAR(cpumask_t, AUTOMATIC) ipi_mask;
    uint32_t cpu_id = PInnerQueueGroup->RecCoreId;
    VAR(int, AUTOMATIC) i = 0;

    if (E_OK == GetApplicationState(data->ReceiverId, &app_state)) {
        if (app_state == APPLICATION_RESTARTING \
            && GetApplicationID() != data->ReceiverId) {
            return IOC_E_NOK;
        }
        if (app_state == APPLICATION_TERMINATED) {
            return IOC_E_NOK;
        }
    }
    
    accurate_inc(&IpiIocCount[cpu_id]);
    accurate_inc(&PInnerQueueGroup->IpiCount);
    if (cpu_id < MAX_CPUS && cpu_id >= 0) {
        for (i = 0; i < MAX_CPUS; i++) {
            cpumask_clear_cpu(i, &ipi_mask);
        }
        cpumask_set_cpu(cpu_id, &ipi_mask);
        arch_raise_ipi(&ipi_mask, IPI_IOC);
    } else {
        return IOC_E_NOK;
    }

    return IOC_E_OK;
    
}

FUNC(Std_ReturnType, OS_CODE) IocSend(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) InnerQueue;
    VAR(Std_ReturnType,  AUTOMATIC) ret;

    ret = SenderParameterCheck(data);
    if (ret != IOC_E_OK) {
        return ret;
    }
    if (data->ElementNum > 1) {
        InnerQueue = &group_queue[data->IocId];
    } else {
        InnerQueue = &nogroup_queue[data->IocId];
    }
    ret = IocPushElement(data, &InnerQueue->RingInfo, true);
    if (ret != IOC_E_OK) {
        return ret;
    }
    if (InnerQueue->FunctionCb != NULL) {
        TriggerRecCb(data, InnerQueue);
    }
    return ret;
}

FUNC(Std_ReturnType, OS_CODE) IocReceive(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) InnerQueue;
    VAR(Std_ReturnType,  AUTOMATIC) ret;
    ret = RecParameterCheck(data);
    if (ret != IOC_E_OK) {
        return ret;
    }
    if (data->ElementNum > 1) {
        InnerQueue = &group_queue[data->IocId];
    } else {
        InnerQueue = &nogroup_queue[data->IocId];
    }

    return IocPopElement(data, &InnerQueue->RingInfo, true);
}

FUNC(Std_ReturnType, OS_CODE) IocWrite(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) InnerQueue;
    VAR(Std_ReturnType,  AUTOMATIC) ret;

    ret = SenderParameterCheck(data);
    if (ret != IOC_E_OK) {
        return ret;
    }
    if (data->ElementNum > 1) {
        InnerQueue = &group_noqueue[data->IocId];
    } else {
        InnerQueue = &nogroup_noqueue[data->IocId];
    }
    ret = IocPushElement(data, &InnerQueue->RingInfo, false);
    if (ret != IOC_E_OK) {
        return ret;
    }
    if (InnerQueue->FunctionCb != NULL) {
        ret = TriggerRecCb(data, InnerQueue);
    }
    return ret;
}

FUNC(Std_ReturnType, OS_CODE) IocRead(
    P2CONST(struct IocData, AUTOMATIC, OS_CONST) data)
{
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) InnerQueue;
    VAR(Std_ReturnType,  AUTOMATIC) ret;

    ret = RecParameterCheck(data);
    if (ret != IOC_E_OK) {
        return ret;
    }

    if (data->ElementNum > 1) {
        InnerQueue = &group_noqueue[data->IocId];
    } else {
        InnerQueue = &nogroup_noqueue[data->IocId];
    }
    ret = IocPopElement(data, &InnerQueue->RingInfo, false);
    
    return ret;
}


FUNC(void, OS_CODE) IocEmptyQueue(
    VAR(uint16, AUTOMATIC) IocId, 
    VAR(bool, AUTOMATIC) group)
{
    P2VAR(struct InnerQueueGroup, AUTOMATIC, OS_APPL_DATA) InnerQueue;
    
    if (group) {
        InnerQueue = &group_queue[IocId];
    } else {
        InnerQueue = &nogroup_queue[IocId];
    }
    ring_empty(&InnerQueue->RingInfo);
}


static FUNC(void, OS_CODE) call_ioc_cb(struct InnerQueueGroup * p, VAR(int, AUTOMATIC) size) {
    VAR(CoreIdType, AUTOMATIC) RecCoreId = GetCoreID();
    VAR(int, AUTOMATIC) i = 0;
	for (i = 0; i < size; i++) {
		while (accurate_read(&p[i].IpiCount) > 0 && p[i].FunctionCb && RecCoreId == p[i].RecCoreId) {
			accurate_dec(&p[i].IpiCount);
			p[i].FunctionCb();
		}
	}
}

/**
 * 由于ipi不是中断嵌套的，不用担心中断丢失的问题
*/
static FUNC(void, OS_CODE) SmpIocIsr(void)
{
	VAR(CoreIdType, AUTOMATIC) cpu_id = GetCoreID();
	VAR(int, AUTOMATIC) count;
	/**
	 * 下一轮循环判断如果原子计数发生了变化，表明了在处理函数期间又发生了核间通讯
	 * 表明仍然有未处理的回调函数
	*/
	do {
		count = accurate_read(&IpiIocCount[cpu_id]);
		/**
		 * 遍历此核心上的所有IOC回调
		*/
		call_ioc_cb(group_queue, get_array_count(group_queue));
		call_ioc_cb(nogroup_queue, get_array_count(nogroup_queue));
		call_ioc_cb(nogroup_noqueue, get_array_count(nogroup_noqueue));
		call_ioc_cb(group_noqueue, get_array_count(group_noqueue));
	} while (accurate_cmpxchg(&IpiIocCount[cpu_id], count, 0) != count);
}
#endif
